home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / network / base / netkit-b.07a / netkit-b / NetKit-B-0.07A / tftp / tftp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-20  |  10.7 KB  |  431 lines

  1. /*
  2.  * Copyright (c) 1983 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  */
  33.  
  34. /*
  35.  * From: @(#)tftp.c    5.10 (Berkeley) 3/1/91
  36.  */
  37. char tftp_rcsid[] = 
  38.   "$Id: tftp.c,v 1.4 1996/07/20 21:04:16 dholland Exp $";
  39.  
  40. /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
  41.  
  42. /*
  43.  * TFTP User Program -- Protocol Machines
  44.  */
  45. #include <sys/types.h>
  46. #include <sys/socket.h>
  47. #include <sys/time.h>
  48.  
  49. #include <netinet/in.h>
  50. #include <arpa/tftp.h>
  51.  
  52. #include <signal.h>
  53. #include <stdio.h>
  54. #include <errno.h>
  55. #include <setjmp.h>
  56. #include <unistd.h>
  57.  
  58. void startclock(void);
  59. int makerequest(int request, char *name, struct tftphdr *tp, char *mode);
  60. void nak(int errnor);
  61. void tpacket(char *s, struct tftphdr *tp, int n);
  62. void ahead(void);
  63. int synchnet(int);
  64. void stopclock(void);
  65. void printstats(char *direction, unsigned long amount);
  66.  
  67. int readit(FILE *file, struct tftphdr **dpp, int convert);
  68. int writeit(FILE *file, struct tftphdr **dpp, int ct, int convert);
  69. void read_ahead(FILE *file, int convert /* if true, convert to ascii */);
  70. void write_behind(FILE *file, int convert);
  71.  
  72. extern  struct sockaddr_in s_in;         /* filled in by main */
  73. extern  int     f;                      /* the opened socket */
  74. extern  int     trace;
  75. extern  int     verbose;
  76. extern  int     rexmtval;
  77. extern  int     maxtimeout;
  78.  
  79. #define PKTSIZE    SEGSIZE+4
  80. char    ackbuf[PKTSIZE];
  81. int    timeout;
  82. extern sigjmp_buf    toplevel;
  83. sigjmp_buf    timeoutbuf;
  84.  
  85. void
  86. timer()
  87. {
  88.     timeout += rexmtval;
  89.     if (timeout >= maxtimeout) {
  90.         printf("Transfer timed out.\n");
  91.         siglongjmp(toplevel, -1);
  92.     }
  93.     siglongjmp(timeoutbuf, 1);
  94. }
  95.  
  96. /*
  97.  * Send the requested file.
  98.  */
  99. void
  100. sendfile(int fd, char *name, char *mode)
  101. {
  102.     register struct tftphdr *ap;       /* data and ack packets */
  103.     struct tftphdr *r_init(), *dp;
  104.     volatile int block = 0, size = 0;
  105.     int n;
  106.     volatile unsigned long amount = 0;
  107.     struct sockaddr_in from;
  108.     int fromlen;
  109.     volatile int convert;            /* true if doing nl->crlf conversion */
  110.     FILE *file;
  111.  
  112.     startclock();           /* start stat's clock */
  113.     dp = r_init();          /* reset fillbuf/read-ahead code */
  114.     ap = (struct tftphdr *)ackbuf;
  115.     file = fdopen(fd, "r");
  116.     convert = !strcmp(mode, "netascii");
  117.  
  118.     signal(SIGALRM, timer);
  119.     do {
  120.         if (block == 0)
  121.             size = makerequest(WRQ, name, dp, mode) - 4;
  122.         else {
  123.         /*      size = read(fd, dp->th_data, SEGSIZE);   */
  124.             size = readit(file, &dp, convert);
  125.             if (size < 0) {
  126.                 nak(errno + 100);
  127.                 break;
  128.             }
  129.             dp->th_opcode = htons((u_short)DATA);
  130.             dp->th_block = htons((u_short)block);
  131.         }
  132.         timeout = 0;
  133.         (void) sigsetjmp(timeoutbuf, 1);
  134. send_data:
  135.         if (trace)
  136.             tpacket("sent", dp, size + 4);
  137.         n = sendto(f, dp, size + 4, 0,
  138.             (struct sockaddr *)&s_in, sizeof (s_in));
  139.         if (n != size + 4) {
  140.             perror("tftp: sendto");
  141.             goto abort;
  142.         }
  143.         read_ahead(file, convert);
  144.         for ( ; ; ) {
  145.             alarm(rexmtval);
  146.             do {
  147.                 fromlen = sizeof (from);
  148.                 n = recvfrom(f, ackbuf, sizeof (ackbuf), 0,
  149.                     (struct sockaddr *)&from, &fromlen);
  150.             } while (n <= 0);
  151.             alarm(0);
  152.             if (n < 0) {
  153.                 perror("tftp: recvfrom");
  154.                 goto abort;
  155.             }
  156.             s_in.sin_port = from.sin_port;   /* added */
  157.             if (trace)
  158.                 tpacket("received", ap, n);
  159.             /* should verify packet came from server */
  160.             ap->th_opcode = ntohs(ap->th_opcode);
  161.             ap->th_block = ntohs(ap->th_block);
  162.             if (ap->th_opcode == ERROR) {
  163.                 printf("Error code %d: %s\n", ap->th_code,
  164.                     ap->th_msg);
  165.                 goto abort;
  166.             }
  167.             if (ap->th_opcode == ACK) {
  168.                 volatile int j = 0;
  169.  
  170.                 if (ap->th_block == block) {
  171.                     break;
  172.                 }
  173.                 /* On an error, try to synchronize
  174.                  * both sides.
  175.                  */
  176.                 j = synchnet(f);
  177.                 if (j && trace) {
  178.                     printf("discarded %d packets\n",
  179.                             j);
  180.                 }
  181.                 if (ap->th_block == (block-1)) {
  182.                     goto send_data;
  183.                 }
  184.             }
  185.         }
  186.         if (block > 0)
  187.             amount += size;
  188.         block++;
  189.     } while (size == SEGSIZE || block == 1);
  190. abort:
  191.     fclose(file);
  192.     stopclock();
  193.     if (amount > 0)
  194.         printstats("Sent", amount);
  195. }
  196.  
  197. /*
  198.  * Receive a file.
  199.  */
  200. void
  201. recvfile(int fd, char *name, char *mode)
  202. {
  203.     register struct tftphdr *ap;
  204.     struct tftphdr *dp, *w_init();
  205.     volatile int block = 1, size = 0;
  206.     int n; 
  207.     volatile unsigned long amount = 0;
  208.     struct sockaddr_in from;
  209.     int fromlen;
  210.     volatile int firsttrip = 1;
  211.     FILE *file;
  212.     volatile int convert;            /* true if converting crlf -> lf */
  213.  
  214.     startclock();
  215.     dp = w_init();
  216.     ap = (struct tftphdr *)ackbuf;
  217.     file = fdopen(fd, "w");
  218.     convert = !strcmp(mode, "netascii");
  219.  
  220.     signal(SIGALRM, timer);
  221.     do {
  222.         if (firsttrip) {
  223.             size = makerequest(RRQ, name, ap, mode);
  224.             firsttrip = 0;
  225.         } else {
  226.             ap->th_opcode = htons((u_short)ACK);
  227.             ap->th_block = htons((u_short)(block));
  228.             size = 4;
  229.             block++;
  230.         }
  231.         timeout = 0;
  232.         (void) sigsetjmp(timeoutbuf, 1);
  233. send_ack:
  234.         if (trace)
  235.             tpacket("sent", ap, size);
  236.         if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&s_in,
  237.             sizeof (s_in)) != size) {
  238.             alarm(0);
  239.             perror("tftp: sendto");
  240.             goto abort;
  241.         }
  242.         write_behind(file, convert);
  243.         for ( ; ; ) {
  244.             alarm(rexmtval);
  245.             do  {
  246.                 fromlen = sizeof (from);
  247.                 n = recvfrom(f, dp, PKTSIZE, 0,
  248.                     (struct sockaddr *)&from, &fromlen);
  249.             } while (n <= 0);
  250.             alarm(0);
  251.             if (n < 0) {
  252.                 perror("tftp: recvfrom");
  253.                 goto abort;
  254.             }
  255.             s_in.sin_port = from.sin_port;   /* added */
  256.             if (trace)
  257.                 tpacket("received", dp, n);
  258.             /* should verify client address */
  259.             dp->th_opcode = ntohs(dp->th_opcode);
  260.             dp->th_block = ntohs(dp->th_block);
  261.             if (dp->th_opcode == ERROR) {
  262.                 printf("Error code %d: %s\n", dp->th_code,
  263.                     dp->th_msg);
  264.                 goto abort;
  265.             }
  266.             if (dp->th_opcode == DATA) {
  267.                 volatile int j = 0;
  268.  
  269.                 if (dp->th_block == block) {
  270.                     break;          /* have next packet */
  271.                 }
  272.                 /* On an error, try to synchronize
  273.                  * both sides.
  274.                  */
  275.                 j = synchnet(f);
  276.                 if (j && trace) {
  277.                     printf("discarded %d packets\n", j);
  278.                 }
  279.                 if (dp->th_block == (block-1)) {
  280.                     goto send_ack;  /* resend ack */
  281.                 }
  282.             }
  283.         }
  284.     /*      size = write(fd, dp->th_data, n - 4); */
  285.         size = writeit(file, &dp, n - 4, convert);
  286.         if (size < 0) {
  287.             nak(errno + 100);
  288.             break;
  289.         }
  290.         amount += size;
  291.     } while (size == SEGSIZE);
  292. abort:                                          /* ok to ack, since user */
  293.     ap->th_opcode = htons((u_short)ACK);    /* has seen err msg */
  294.     ap->th_block = htons((u_short)block);
  295.     (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&s_in, sizeof (s_in));
  296.     write_behind(file, convert);            /* flush last buffer */
  297.     fclose(file);
  298.     stopclock();
  299.     if (amount > 0)
  300.         printstats("Received", amount);
  301. }
  302.  
  303. int
  304. makerequest(int request, char *name, struct tftphdr *tp, char *mode)
  305. {
  306.     register char *cp;
  307.  
  308.     tp->th_opcode = htons((u_short)request);
  309.     cp = tp->th_stuff;
  310.     strcpy(cp, name);
  311.     cp += strlen(name);
  312.     *cp++ = '\0';
  313.     strcpy(cp, mode);
  314.     cp += strlen(mode);
  315.     *cp++ = '\0';
  316.     return (cp - (char *)tp);
  317. }
  318.  
  319. struct errmsg {
  320.     int    e_code;
  321.     char    *e_msg;
  322. } errmsgs[] = {
  323.     { EUNDEF,    "Undefined error code" },
  324.     { ENOTFOUND,    "File not found" },
  325.     { EACCESS,    "Access violation" },
  326.     { ENOSPACE,    "Disk full or allocation exceeded" },
  327.     { EBADOP,    "Illegal TFTP operation" },
  328.     { EBADID,    "Unknown transfer ID" },
  329.     { EEXISTS,    "File already exists" },
  330.     { ENOUSER,    "No such user" },
  331.     { -1,        0 }
  332. };
  333.  
  334. /*
  335.  * Send a nak packet (error message).
  336.  * Error code passed in is one of the
  337.  * standard TFTP codes, or a UNIX errno
  338.  * offset by 100.
  339.  */
  340. void
  341. nak(int error)
  342. {
  343.     register struct errmsg *pe;
  344.     register struct tftphdr *tp;
  345.     int length;
  346.  
  347.     tp = (struct tftphdr *)ackbuf;
  348.     tp->th_opcode = htons((u_short)ERROR);
  349.     tp->th_code = htons((u_short)error);
  350.     for (pe = errmsgs; pe->e_code >= 0; pe++)
  351.         if (pe->e_code == error)
  352.             break;
  353.     if (pe->e_code < 0) {
  354.         pe->e_msg = strerror(error - 100);
  355.         tp->th_code = EUNDEF;
  356.     }
  357.     strcpy(tp->th_msg, pe->e_msg);
  358.     length = strlen(pe->e_msg) + 4;
  359.     if (trace)
  360.         tpacket("sent", tp, length);
  361.     if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&s_in,
  362.         sizeof (s_in)) != length)
  363.         perror("nak");
  364. }
  365.  
  366. void
  367. tpacket(char *s, struct tftphdr *tp, int n)
  368. {
  369.     static char *opcodes[] =
  370.        { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
  371.     register char *cp, *file;
  372.     u_short op = ntohs(tp->th_opcode);
  373.     char *index();
  374.  
  375.     if (op < RRQ || op > ERROR)
  376.         printf("%s opcode=%x ", s, op);
  377.     else
  378.         printf("%s %s ", s, opcodes[op]);
  379.     switch (op) {
  380.  
  381.     case RRQ:
  382.     case WRQ:
  383.         n -= 2;
  384.         file = cp = tp->th_stuff;
  385.         cp = index(cp, '\0');
  386.         printf("<file=%s, mode=%s>\n", file, cp + 1);
  387.         break;
  388.  
  389.     case DATA:
  390.         printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
  391.         break;
  392.  
  393.     case ACK:
  394.         printf("<block=%d>\n", ntohs(tp->th_block));
  395.         break;
  396.  
  397.     case ERROR:
  398.         printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
  399.         break;
  400.     }
  401. }
  402.  
  403. struct timeval tstart;
  404. struct timeval tstop;
  405. struct timezone zone;
  406.  
  407. void
  408. startclock(void) {
  409.     gettimeofday(&tstart, &zone);
  410. }
  411.  
  412. void
  413. stopclock(void) {
  414.     gettimeofday(&tstop, &zone);
  415. }
  416.  
  417. void
  418. printstats(char *direction, unsigned long amount)
  419. {
  420.     double delta;
  421.             /* compute delta in 1/10's second units */
  422.     delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
  423.         ((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
  424.     delta = delta/10.;      /* back to seconds */
  425.     printf("%s %ld bytes in %.1f seconds", direction, amount, delta);
  426.     if (verbose)
  427.         printf(" [%.0f bits/sec]", (amount*8.)/delta);
  428.     putchar('\n');
  429. }
  430.  
  431.